package edu.gatech.coc.sirana.graph;

import java.util.ArrayList;
import java.util.Collection;

import net.util.Debug;

import edu.gatech.coc.sirana.exception.SIRANAException;
import edu.gatech.coc.sirana.parser.ClientLogItem;
import edu.gatech.coc.sirana.util.CONSTANT;

public class InteractionCycle  implements Comparable<InteractionCycle> {
	/**
	 *  
	 * The last node is the node where the back edge happens.
	 */
	private ArrayList<ClientLogItem> items;
	
	/**
	 * 
	 * @param nodesOnCircle The first node is the start node of the circel, but the last node is the end node from where the backedge goes out.
	 */
	public InteractionCycle(ArrayList<ClientLogItem> nodesOnCircle){
		this.setItems(nodesOnCircle);		
	}
	
	public ClientLogItem findHeadItem(){
		return this.items.get(0);
	}
		
	public ClientLogItem findTailItem(){
		return this.items.get(this.items.size()-1);
	}
	
	public ArrayList<ClientLogItem> getItems() {
		return this.items;
	}
	
	public void setItems(ArrayList<ClientLogItem> circle) {
		this.items = circle;
	}
	
	public ArrayList<String> findAllAction(){
		ArrayList<String> allActions	= new ArrayList<String>();
		
		for (ClientLogItem clip : this.getItems()){
			allActions.add(clip.findAction());
		}
		
		return allActions;
	}
	
	public ArrayList<String> finaAllEvent(){
		ArrayList<String> allEvents = new ArrayList<String>();
		
		for (ClientLogItem clip : this.items){
			allEvents.add(clip.getEvent());
		}
		
		return allEvents;
	}
	
	public String findItemIds(){
		StringBuffer sb	= new StringBuffer();
		for (ClientLogItem clip : items){
			sb.append(clip.getId() + "-");
		}
		return sb.toString();
	}
	
	/**
	 * The head node's behavior and the backedge node's behavior
	 * @return
	 */
	public String findHeadTailAction(){
//		Debug.println( "Size of item " + this.getItems().size() );
		String headAct = this.findHeadItem().findAction();
		String tailAct = this.findTailItem().findAction();
		if (this.getItems().size() == 1) return headAct;
		else return headAct + "+" + tailAct;
	}
	
	public int findNumNodesOnCircle(){
		return this.items.size();
	}
	
	public String toTimestampString(){
		StringBuffer sb	= new StringBuffer();
		for(ClientLogItem n : this.items){
			sb.append(n.getTimeStamp() + "-");
		}
		return sb.toString();
	}
	
	public String toString(){
		StringBuffer sb	= new StringBuffer();
		sb.append( "key=" + this.findHeadTailAction() + "\n");
		for(ClientLogItem n : this.items){
			sb.append(n.toString() + "\n");
		}
		
		return sb.substring(0, sb.length()-2).toString();
	}

	@Override
	public int compareTo(InteractionCycle c) {
		int thisHeadId = this.items.get(0).getId();
		int cHeadId	= c.getItems().get(0).getId();
		
		if (thisHeadId > cHeadId) return 1;
		else if (thisHeadId == cHeadId) return 0;
		else return -1;
	}
	
	public boolean hasSameAction(InteractionCycle c){
		if (findNumNodesOnCircle()!=c.findNumNodesOnCircle()) return false;
		
		for (int i=0; i<this.items.size(); i++){
			String thisAction	= this.items.get(i).findAction();
			String cAction		= c.getItems().get(i).findAction();
			
			if (thisAction.compareTo(cAction)!=0) return false;
		}
		
		return true;
	}
	
	public boolean hasSameItems(InteractionCycle c){
		if (findNumNodesOnCircle()!=c.findNumNodesOnCircle()) return false;
		
		for (int i=0; i<this.items.size(); i++){
			ClientLogItem thisAction	= this.items.get(i);
			ClientLogItem cAction		= c.getItems().get(i);
			
			if (thisAction.compareTo(cAction)!=0) return false;
		}
		
		return true;
	}

	public boolean hasSameBackEdge(InteractionCycle c){
		return this.findHeadTailAction().equals(c.findHeadTailAction());
	}
	
	/**
	 * 
	 * @param c
	 * @param og
	 * 
	 * 
	 */
	public boolean containNodesOf(InteractionCycle c, OnlineGraph og){
		ArrayList<FDANode> thisNodes = og.findNodeGroupByItems(this.getItems());
		ArrayList<FDANode> cNodes	= og.findNodeGroupByItems(c.getItems());

		for (FDANode n: cNodes){
			if (!thisNodes.contains(n)) return false;
		}
		
		return true;
	}
	

	public boolean isIn(Collection<InteractionCycle> cycleList){
		for (InteractionCycle ic : cycleList){
			if (this.hasSameItems(ic)) return true;
		}
		return false;
	}
		
	public boolean isSuccessiveTo(InteractionCycle c){
		int thisHeadId 	= this.findHeadItem().getId();
		int cTailId		= c.findTailItem().getId();
		if ((thisHeadId - cTailId) == 1)
			return true;
		else 
			return false;
	}
	
	
	
	public boolean isApproximateRSCycleTo(InteractionCycle c, OnlineGraph og){
		boolean is = false;
		if ( this.isSuccessiveTo(c) && this.hasSameBackEdge(c) && c.containNodesOf(this, og) ){
			is = true;
		}
		
		Debug.println("...isApproximateRSCycleTo? \n\t" + this.toString() + "\n\t" + c.toString() + "\n\t" + is);
		
		return is;
	}
	
	public boolean isExactRSCycleTo(InteractionCycle c){
		boolean isSuccessiveTo = this.isSuccessiveTo(c);
		boolean isRepeatedCycle = this.hasSameAction(c);
		
		return isSuccessiveTo && isRepeatedCycle;
	}
	
	
	/**
	 * 
	 * @return ture if there is any interaction event.
	 */
	public boolean isInteractiveCycle(){
		return true;
//		boolean is=false;
//				
//		for (ClientLogItem clip : this.getItems()){			
//			if (clip.hasInteractiveEvent()){
//				is= true;
//				break;
//			}
//		}
//
////		Debug.println(this + "\n\t interactive cycle?" + is);
//		return is;
	}
	
}
